#!/usr/bin/env python
#
# Simulated grid Computing Element (CE or Site), which provides an interface
# to a single scheduler on a cluster
#
# Author:   Mike Murphy <mamurph@cs.clemson.edu>
# Revision: 23 September 2009
#
#   Copyright 2009 Clemson University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# This material is based upon work supported under a National Science
# Foundation Graduate Research Fellowship. Any opinions, findings,
# conclusions or recommendations expressed in this publication are those of
# the author(s) and do not necessarily reflect the views of the National
# Science Foundation.

'''
Simulated Computing Element (CE) components. In the simulated grid, a CE is
realized as an instance of the Site class. Each Site is a named interface
to a single scheduler on a cluster.
'''


class Site(object):
   '''
   A Site is a named interface to a single scheduler on a cluster. Two or
   more Sites may share the same cluster and even the same scheduler on the
   same cluster. Sites are aware of two sets of metrics: the scheduler
   metrics, which measure queue stats for the associated scheduler, and the
   cluster metrics, which measure queue stats for all schedulers on the
   associated cluster.
   '''
   def __init__(self, name, sched, cluster, **kwds):
      '''
      @param name: Name of the Site
      @type name: str
      @param sched: Scheduler instance to which Site jobs are sent
      @param cluster: Cluster instance associated with the site
      @param kwds: Site extra data
      '''
      self.name = name
      self.sched = sched
      self.cluster = cluster
      self.extra_data = kwds
   def submit_job(self, job):
      '''
      Submits a job to the scheduler used by the Site. The job will be tagged
      with the site_name field added to the job extra_data.

      @param job: Job object to be submitted

      @return: a JobSubmitResult with submission information
      '''
      job.extra_data['site_name'] = self.name
      return self.sched.submit_job(job)
   def terminate(self):
      '''
      Kills all jobs submitted to the associated scheduler by this Site,
      allowing the Site to be removed from the grid.
      '''
      self.sched.kill_all('site_name', self.name)
   #
   # These methods query the SCHEDULER:
   def get_waiting_count(self):
      '''
      @rtype: int
      @return: number of jobs waiting in the queue of the scheduler
      used by this Site. This count may include jobs submitted to the same
      scheduler by other Sites or other components.
      '''
      return self.sched.get_waiting_count()
   def get_running_count(self):
      '''
      @rtype: int
      @return: number of jobs running, as reported by the scheduler
      associated with the Site. This result may include jobs submitted to
      the same scheduler by other Sites or other components.
      '''
      return self.sched.get_running_count()
   def get_total_queue_size(self):
      '''
      @rtype: int
      @return: total queue size (waiting+running) as reported by the
      scheduler associated with the Site. This size may include jobs submitted
      to the same scheduler by other Sites or other components.
      '''
      return self.sched.get_total_queue_size()
   def get_completed_count(self):
      '''
      @rtype: int
      @return: total number of jobs completed by the scheduler associated
      with the site. This number may include jobs submitted to the same
      scheduler by other sites or other components.
      '''
      return self.sched.get_completed_count()
   def get_cpu_count(self):
      '''
      @rtype: int
      @return: total number of targets currently registered with the
      associated scheduler
      '''
      return self.sched.get_cpu_count()
   def get_idle_count(self):
      '''
      @rtype: int
      @return: total number of free cores as reported by the associated
      scheduler
      '''
      return self.sched.get_free_count()
   #
   # Same queries on the CLUSTER (all schedulers):
   def get_cluster_waiting_count(self):
      '''
      @rtype: int
      @return: total number of jobs waiting across all schedulers on the
      associated cluster
      '''
      return self.cluster.get_waiting_count()
   def get_cluster_running_count(self):
      '''
      @rtype: int
      @return: total number of jobs running, as reported by all schedulers
      on the associated cluster
      '''
      return self.cluster.get_running_count()
   def get_cluster_total_queue_size(self):
      '''
      @rtype: int
      @return: total of the queue sizes (waiting+running) of all schedulers
      on the associated cluster
      '''
      return self.cluster.get_total_queue_size()
   def get_cluster_completed_count(self):
      '''
      @rtype: int
      @return: total number of jobs completed, as reported by all schedulers
      on the associated cluster
      '''
      return self.cluster.get_completed_count()
   def get_cluster_cpu_count(self):
      '''
      @rtype: int
      @return: total number of CPU cores present on the associated cluster
      '''
      return self.cluster.get_cpu_count()
   def get_cluster_idle_count(self):
      '''
      @rtype: int
      @return: total number of idle CPU cores on the associated cluster
      '''
      return self.cluster.get_actual_idle_count()
#

if __name__ == '__main__':
   from cluster_sim import Cluster
   from condor_sim import Condor
   from job_model import Job
   from kernel import Simulator
   from scheduler import Scheduler

   sim = Simulator()
   c1 = Cluster('cluster1', sim)
   c1.grow(10)
   sched1 = Condor(sim)
   c1.add_scheduler('Scheduler', sched1)
   sched1.add_targets(c1.machines)

   c2 = Cluster('cluster2', sim)
   c2.grow(5)
   sched2 = Scheduler(sim)
   c2.add_scheduler('Scheduler', sched2)
   sched2.add_targets(c2.machines)

   site1a = Site('site1a', sched1, c1)
   site1b = Site('site1b', sched1, c1)
   site2 = Site('site2', sched2, c2)

   for x in xrange(0,20):
      job = Job(20 + (x % 4), name='job%d' % x, prob_err=0.2)
      if (x % 2) != 0:
         if x in (3, 7, 11):
            sim.new_external_event(10, site1b.submit_job, job)
         else:
            sim.new_external_event(10, site1a.submit_job, job)
      else:
         sim.new_external_event(10, site2.submit_job, job)
   #

   sim.run()
#
